/*
 *	dfclient_rget.c
 *	© 2009 DigiFi Limited
 *
 *	Contains the functions that deal with rgets
 *
 */

#include "../headers/dfrget.h"

#include "util/stringext.h"

struct dfrget_pointers {
	void (*albumartist)(df_albumartist*);
	void (*detailedtrackinfo)(df_detailedtrack*);
	void (*lastplayererror)(char*);
	void (*playerstatus)(char*);
	void (*playingchecksum)(char*);
	void (*repeat)(int);
	void (*shuffle)(int);
	void (*trackname)(df_trackname*);
	void (*trackposition)(df_time*);
};

// shared functions
int df_initrgets(void);

// local functions
static void dfrget_threadstart(void);
static char* dfrget_findcommand(int id);
static int dfrget_handlecallback(char *name, char *command);
static int dfrget_randomiseport();


static char *rget_buffer;
static int rget_buffer_len;

static int rget_port;

static int running;
static int thread_status;
static pthread_t rget_thread;

// struct of the pointers to call back to
struct dfrget_pointers ptrs;

static void (*callback_ptr)(int);

int dfrget_init(void (*callback)(int)) {
	
	rget_port = dfrget_randomiseport();
	
	dferrors_printdebug("Starting rget thread on %d\n", rget_port);
	
	callback_ptr = callback;
	
	thread_status =0;
	running = 0;
	
	rget_buffer = malloc(sizeof(char));
	rget_buffer[0] = '\0';
	rget_buffer_len=0;
	
	// setup the rget thread
	pthread_create(&rget_thread, NULL, (void *(*)(void*))dfrget_threadstart, NULL);
	
	return 0;
	
}

int dfrget_connected(void) {
	return thread_status;
}


int dfrget_enable(void) {
	// make a call to register the rget thread with sophia
	return dfmultithread_queue_rget("[RGetRegisterClient \"%d\" \"%s\"]", rget_port, dfsocket_getlocalip());
}

int dfrget_disable(void) {
	dfrget_bufferflush();
	
	//return dfrget_clearcommands();
	return 0;
}
 
void dfrget_bufferflush(void) {
	rget_buffer = memset(rget_buffer,0,rget_buffer_len);
	free(rget_buffer);
	
	rget_buffer = malloc(sizeof(char));
	rget_buffer[0] = '\0';
	rget_buffer_len=0; 
}

void dfrget_bufferdump(void) {
	dferrors_printdebug("RGet buffer: %s\n", rget_buffer);
}

void dfrget_dump(void) {
	
}


int dfrget_shutdown(void) {
	dferrors_printdebug("Shutting down rget thread\n");
	
	running = 0;
	while (thread_status != 0) {dferrors_printdebug("."); sleep(1);}
	
	dferrors_printdebug(" Done.\n");
	
	return 0;
}






int dfrget_albumartist(unsigned int room_id, void (*callback)(df_albumartist*)){
	ptrs.albumartist = callback;
	return dfmultithread_queue_rget("[RGetAlbumArtist %u]", room_id);
}


int dfrget_clearcommands(unsigned int room_id){
	return dfmultithread_queue_rget("[RGetClearCommands %u]", room_id);
}


int dfrget_detailedtrackinfo(unsigned int room_id, void (*callback)(df_detailedtrack*)){
	ptrs.detailedtrackinfo = callback;
	return dfmultithread_queue_rget("[RGetDetailedTrackInfo %u]", room_id);
}


int dfrget_lastplayererror(unsigned int room_id, void (*callback)(char*)){
	ptrs.lastplayererror = callback;
	return dfmultithread_queue_rget("[RGetLastPlayerError %u]", room_id);
}


int dfrget_playerstatus(unsigned int room_id, void (*callback)(char*)){
	ptrs.playerstatus = callback;
	return dfmultithread_queue_rget("[RGetPlayerStatus %u]", room_id);
}


int dfrget_playingchecksum(unsigned int room_id, void (*callback)(char*)){
	ptrs.playingchecksum = callback;
	return dfmultithread_queue_rget("[RGetPlayingChecksum %u]", room_id);
}


int dfrget_repeat(unsigned int room_id, void (*callback)(int)){
	ptrs.repeat = callback;
	return dfmultithread_queue_rget("[RGetRepeat %u]", room_id);
}


int dfrget_shuffle(unsigned int room_id, void (*callback)(int)){
	ptrs.shuffle = callback;
	return dfmultithread_queue_rget("[RGetShuffle %u]", room_id);
}


int dfrget_trackname(unsigned int room_id, void (*callback)(df_trackname*)){
	ptrs.trackname = callback;
	return dfmultithread_queue_rget("[RGetTrackName %u]", room_id);
}


int dfrget_trackposition(unsigned int room_id, void (*callback)(df_time*)){
	ptrs.trackposition = callback;
	return dfmultithread_queue_rget("[RGetTrackPosition %u]", room_id);
}









static void dfrget_threadstart() {
	
	int server_id, socket_id;
	char *command;
	dfrx_result *rx;
	int err;
	
	// bind to a socket
	server_id = dfsocket_listen(rget_port);
	
	running = 1;
	
	// if there is a callback on connect execute it
	if (callback_ptr != NULL) { callback_ptr(0); callback_ptr == NULL;}
	
	// accept sophia when it tries to connect
	socket_id = -1;
	while (socket_id <=0 && running) { socket_id = dfsocket_accept(server_id); }
	
	dferrors_printdebug("Sophia connected to RGet socket.\n");
	
	// set non-blocking
	dfsocket_set_nonblocking(socket_id);
	
	thread_status = 1;
	
	// go into a loop waiting for input
	while (running) {
		
		// go find a command
		if ((command = dfrget_findcommand(socket_id)) == NULL) { sleep(1); continue; }
		
		// retrieve the name of the incoming rget
		rx = dfregex_matches(command, "findname", DF_RGET_COMMAND);
		
		// pass the name and the command to the handler
		//dferrors_printdebug("rget: %s\nrx: %s", command, (rx!=NULL)?rx->value:"(null)");
		
		if (rx!=NULL) { 
			if ((err = dfrget_handlecallback(rx->subexps[1].value, rx->subexps[0].value)) <0) {
				dferrors_printerror(err, "Failed to handle rget.");
			}
			dfregex_freeresult(rx); 
		}
		
		//dfregex_freeresult(rx);
		free(command);
		
	}
	
	// shutdown the socket
	dfsocket_disconnect(socket_id);
	dfsocket_unbind(server_id);
	dfsocket_disconnect(server_id);
	
	thread_status=0;
}



static char* dfrget_findcommand(int id) {
	int received;
	char *out;
	dfrx_result *rx;
	
	received =0;
	
	while (((rx = dfregex_match(rget_buffer, "void", DF_RGET_COMMAND)) == NULL) && running) {
		// poll
		//if (dfsocket_poll(id, DF_RGET_SPIN,1) == 0) {
			
			if ((rget_buffer = realloc(rget_buffer, (rget_buffer_len + 251) * sizeof(char))) == NULL) {dferrors_printerror(-DFCLIENT_ENOMEM, "Failed to realloc the find buffer."); return NULL;}
			
			// if we didnt timeout then go get some data
			received = dfsocket_receive(id, rget_buffer + rget_buffer_len, 250);
			if (received < 0) return NULL;
			
			rget_buffer[rget_buffer_len + received] = '\0';

			rget_buffer_len += received;
		//}
	}
	
	if (!running) {return NULL;}
	
	// resize down to actual
	if ((rget_buffer = realloc(rget_buffer, (rget_buffer_len+1) * sizeof(char))) == NULL) {return NULL;}
	rget_buffer[rget_buffer_len] = '\0';
	
	// cut command out of buffer
	string_extract(&rget_buffer, &out, rx->start, rx->length);
	rget_buffer_len -= rx->length;
	
	return out;
}



static int dfrget_handlecallback(char *name, char *command) {

	dfrx_result *rx;
	df_albumartist *aa;
	df_detailedtrack *dt;
	df_trackname *tn;
	df_time *t;
	char *s;
	int i;

	if (strcmp(name,"GetAlbumArtist") == 0) {
		rx = dfregex_matches(command, "getalbumartist", DF_RGET_COMMAND);
		
		if ((aa = malloc(sizeof(df_albumartist))) == NULL) {return -DFCLIENT_ENOMEM;}
		if ((aa->album = strdup(rx->subexps[2].value)) == NULL) {free(aa); return -DFCLIENT_ENOMEM;}
		if ((aa->artist = strdup(rx->subexps[3].value)) == NULL) {free(aa->album); free(aa);return -DFCLIENT_ENOMEM;}
		
		ptrs.albumartist(aa);
		
		free(aa->album);
		free(aa->artist);
		free(aa);
	}
	else if (strcmp(name,"GetDetailedTrackInfo") == 0) {
		rx = dfregex_matches(command, "getdetailedtrackinfo", DF_RGET_COMMAND);
		
		if ((dt = malloc(sizeof(df_detailedtrack))) == NULL) {return -DFCLIENT_ENOMEM;}
		
		sscanf(rx->subexps[2].value, "%d", &dt->key);
		if ((dt->name = strdup(rx->subexps[3].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		sscanf(rx->subexps[4].value, "%d", &dt->albumkey);
		if ((dt->albumname = strdup(rx->subexps[5].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		sscanf(rx->subexps[6].value, "%d", &dt->artistkey);
		if ((dt->artistname = strdup(rx->subexps[7].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		sscanf(rx->subexps[8].value, "%d", &dt->genrekey);
		if ((dt->genrename = strdup(rx->subexps[9].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		
		if ((dt->length = malloc(sizeof(df_time))) == NULL) {return -DFCLIENT_ENOMEM;}
		sscanf(rx->subexps[10].value,"%u:%u:%u",&(dt->length->hours), &(dt->length->minutes), &(dt->length->seconds));
		
		sscanf(rx->subexps[11].value, "%d", &dt->source);
		if ((dt->path = strdup(rx->subexps[12].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		
		ptrs.detailedtrackinfo(dt);
		
		free(dt->length);
		free(dt->name);
		free(dt->albumname);
		free(dt->artistname);
		free(dt->genrename);
		free(dt);
	}
	else if (strcmp(name,"GetLastPlayerError") == 0) {
		rx = dfregex_matches(command, "getlastplayererror", DF_RGET_COMMAND);
		
		if ((s = strdup(rx->subexps[2].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		
		ptrs.lastplayererror(s);
		
		free(s);
		
	}
	else if (strcmp(name,"GetPlayerStatus") == 0) {
		rx = dfregex_matches(command, "getplayerstatus", DF_RGET_COMMAND);
		
		if ((s = strdup(rx->subexps[2].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		
		ptrs.playerstatus(s);
		
		free(s);
	}
	else if (strcmp(name,"GetPlayingCheckSum") == 0) {
		rx = dfregex_matches(command, "getplayingchecksum", DF_RGET_COMMAND);
		
		if ((s = strdup(rx->subexps[2].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		
		ptrs.playingchecksum(s);
		
		free(s);
		
	}
	else if (strcmp(name,"GetRepeat") == 0) {
		rx = dfregex_matches(command, "getrepeat", DF_RGET_COMMAND);
		
		sscanf(rx->subexps[2].value,"%d",&i);
		
		ptrs.repeat(i);
	}
	else if (strcmp(name,"GetShuffle") == 0) {
		rx = dfregex_matches(command, "getshuffle", DF_RGET_COMMAND);
		
		sscanf(rx->subexps[2].value,"%d",&i);
		
		ptrs.shuffle(i);
	}
	else if (strcmp(name,"GetTrackName") == 0) {
		rx = dfregex_matches(command, "gettrackname", DF_RGET_COMMAND);
		
		if ((tn = malloc(sizeof(df_trackname))) == NULL) {return -DFCLIENT_ENOMEM;}
		
		if ((tn->name = strdup(rx->subexps[2].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		sscanf(rx->subexps[3].value,"%d",&tn->source);
		if ((tn->path = strdup(rx->subexps[4].value)) == NULL) {return -DFCLIENT_ENOMEM;}
		
		ptrs.trackname(tn);
		
		free(tn->name);
		free(tn->path);
		free(tn);
	}
	else if (strcmp(name,"GetTrackPosition") == 0) {
		rx = dfregex_matches(command, "gettrackposition", DF_RGET_COMMAND);
		
		if ((t = malloc(sizeof(df_time))) == NULL) {return -DFCLIENT_ENOMEM;}
		sscanf(rx->subexps[2].value,"%u:%u:%u",&(t->hours), &(t->minutes), &(t->seconds));
		
		ptrs.trackposition(t);
		
		free(t);
	}
	else {
		dferrors_printdebug("unknown rget (%s) received", name);
	}
	
	if (rx!=NULL) dfregex_freeresult(rx);
	
	return 0;
}

static int dfrget_randomiseport() {
	int r;
	
	srand((unsigned int)time(NULL));
	r = (int)(((double)rand()/(double)RAND_MAX) * 1000);
	
	return DF_RGET_PORT + r;
}

